gdkwindow: Remove the internal cairo_surface used for out-of-band painting
authorJasper St. Pierre <jstpierre@mecheye.net>
Fri, 20 Jun 2014 15:55:09 +0000 (11:55 -0400)
committerJasper St. Pierre <jstpierre@mecheye.net>
Sat, 21 Jun 2014 00:41:54 +0000 (20:41 -0400)
Traditionally, the way painting was done in GTK+ was with the
"expose-event" handler, where you'd use GDK methods to do drawing on
your surface. In GTK+ 2.24, we added cairo support with gdk_cairo_create,
so you could paint your graphics with cairo.

Since then, we've added client-side windows, double buffering, the paint
clock, and various other enhancements, and the modern way to do drawing
is to connect to the "draw" signal on GtkWidget, which hands you a
cairo_t. To do double-buffering, the cairo_t we hand you is actually on
a secret surface, not the actual backing store of the window, and when
the draw handler completes we blit it into the main backing store
atomically.

The code to do this is with the APIs gdk_window_begin_paint_region,
which creates the temporary surface, and gdk_window_end_paint which
blits it back into the backing store. GTK+'s implementation of the
"draw" signal uses these APIs.

We've always sort-of supported people calling gdk_cairo_create
"outside" of a begin_paint / end_paint like old times, but then you're
not getting the benefit of double-buffering, and it's harder for GDK to
optimize.

Additionally, newer backends like Mir and Wayland can't actually support
this model, since they're based on double-buffering and swapping buffers
at various points in time. If we hand you a random cairo_t, we have no
idea when is a good time to swap.

Remove support for this.

This is technically a GDK API break: a warning is added in cases where
gdk_cairo_create is called outside of a paint cycle, and the returned
surface is a dummy that won't ever be composited back onto the main
surface. Testing with complex applications like Ardour didn't produce
any warnings.

gdk/broadway/gdkwindow-broadway.c
gdk/gdkinternals.h
gdk/gdkoffscreenwindow.c
gdk/gdkwindow.c
gdk/gdkwindowimpl.h
gdk/quartz/gdkwindow-quartz.c
gdk/wayland/gdkwindow-wayland.c
gdk/win32/gdkwindow-win32.c
gdk/x11/gdkwindow-x11.c

index 65ca990acae79711dd6d8183da0d3a7368b59864..d25829f08f9574c3e1c030975a5cbc5d8cf3c51a 100644 (file)
@@ -368,16 +368,6 @@ _gdk_broadway_window_destroy (GdkWindow *window,
                                       impl->id);
 }
 
-static gboolean
-gdk_window_broadway_resize_cairo_surface (GdkWindow       *window,
-                                         cairo_surface_t *surface,
-                                         gint             width,
-                                         gint             height)
-{
-  /* Image surfaces cannot be resized */
-  return FALSE;
-}
-
 static void
 gdk_broadway_window_destroy_foreign (GdkWindow *window)
 {
@@ -1552,7 +1542,6 @@ gdk_window_impl_broadway_class_init (GdkWindowImplBroadwayClass *klass)
   impl_class->queue_antiexpose = _gdk_broadway_window_queue_antiexpose;
   impl_class->destroy = _gdk_broadway_window_destroy;
   impl_class->destroy_foreign = gdk_broadway_window_destroy_foreign;
-  impl_class->resize_cairo_surface = gdk_window_broadway_resize_cairo_surface;
   impl_class->get_shape = gdk_broadway_window_get_shape;
   impl_class->get_input_shape = gdk_broadway_window_get_input_shape;
   impl_class->beep = gdk_broadway_window_beep;
index 3cc26cc6dd97880cd09453b1a1f40849a0ccfd3d..56c772504880efb9f2c33df97277ee42b0aafff2 100644 (file)
@@ -266,8 +266,6 @@ struct _GdkWindow
   cairo_region_t *shape;
   cairo_region_t *input_shape;
 
-  cairo_surface_t *cairo_surface;
-
   GList *devices_inside;
   GHashTable *device_events;
 
index 74b82bc4c240733e3f2ab8002dea4000c6c7629a..5ee179f86d9c2881b6262a497fe03e8cbfa5897e 100644 (file)
@@ -549,18 +549,6 @@ gdk_offscreen_window_queue_antiexpose (GdkWindow *window,
   return FALSE;
 }
 
-static gboolean
-gdk_offscreen_window_resize_cairo_surface (GdkWindow       *window,
-                                           cairo_surface_t *surface,
-                                           gint             width,
-                                           gint             height)
-{
-  /* No-op.  The surface gets resized in
-   * gdk_offscreen_window_move_resize_internal().
-   */
-  return TRUE;
-}
-
 /**
  * gdk_offscreen_window_set_embedder:
  * @window: a #GdkWindow
@@ -727,7 +715,6 @@ gdk_offscreen_window_class_init (GdkOffscreenWindowClass *klass)
   impl_class->queue_antiexpose = gdk_offscreen_window_queue_antiexpose;
   impl_class->destroy = gdk_offscreen_window_destroy;
   impl_class->destroy_foreign = NULL;
-  impl_class->resize_cairo_surface = gdk_offscreen_window_resize_cairo_surface;
   impl_class->get_shape = NULL;
   impl_class->get_input_shape = NULL;
   impl_class->beep = NULL;
index dc658c3eb6048dd478eebad39154cab67d8cad41..e864606ac07eb987b43422f925a1979cc56ee89d 100644 (file)
@@ -151,12 +151,11 @@ struct _GdkWindowPaint
 {
   cairo_region_t *region;
   cairo_surface_t *surface;
+  gboolean surface_needs_composite;
 };
 
 /* Global info */
 
-static void             gdk_window_drop_cairo_surface (GdkWindow *private);
-
 static void gdk_window_free_paint_stack (GdkWindow *window);
 
 static void gdk_window_finalize   (GObject              *object);
@@ -477,8 +476,6 @@ gdk_window_finalize (GObject *object)
        _gdk_window_destroy (window, TRUE);
     }
 
-  gdk_window_drop_cairo_surface (window);
-
   if (window->impl)
     {
       g_object_unref (window->impl);
@@ -1002,18 +999,6 @@ recompute_visible_regions_internal (GdkWindow *private,
                                              FALSE);
        }
     }
-
-  if (private->cairo_surface)
-    {
-      if (!gdk_window_has_impl (private) ||
-          !GDK_WINDOW_IMPL_GET_CLASS (private->impl)->resize_cairo_surface (private,
-                                                                            private->cairo_surface,
-                                                                            private->width,
-                                                                            private->height))
-        {
-          gdk_window_drop_cairo_surface (private);
-        }
-    }
 }
 
 /* Call this when private has changed in one or more of these ways:
@@ -1531,10 +1516,6 @@ gdk_window_reparent (GdkWindow *window,
   if (is_parent_of (window, new_parent))
     return;
 
-  /* This might be wrong in the new parent, e.g. for non-native surfaces.
-     To make sure we're ok, just wipe it. */
-  gdk_window_drop_cairo_surface (window);
-
   impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
   old_parent = window->parent;
 
@@ -1716,8 +1697,6 @@ gdk_window_ensure_native (GdkWindow *window)
 
   /* Need to create a native window */
 
-  gdk_window_drop_cairo_surface (window);
-
   screen = gdk_window_get_screen (window);
   display = gdk_screen_get_display (screen);
   parent = window->parent;
@@ -1889,7 +1868,6 @@ _gdk_window_destroy_hierarchy (GdkWindow *window,
   if (temp_window == window)
     g_object_set_qdata (G_OBJECT (screen), quark_pointer_window, NULL);
 
-
   switch (window->window_type)
     {
     case GDK_WINDOW_ROOT:
@@ -1987,8 +1965,6 @@ _gdk_window_destroy_hierarchy (GdkWindow *window,
 
          _gdk_window_clear_update_area (window);
 
-         gdk_window_drop_cairo_surface (window);
-
          impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
 
          if (gdk_window_has_impl (window))
@@ -2780,6 +2756,12 @@ gdk_window_begin_paint_region (GdkWindow       *window,
       cairo_surface_get_device_scale (paint->surface, &sx, &sy);
 #endif
       cairo_surface_set_device_offset (paint->surface, -clip_box.x*sx, -clip_box.y*sy);
+
+      paint->surface_needs_composite = TRUE;
+    }
+  else
+    {
+      paint->surface = gdk_window_ref_impl_surface (window);
     }
 
   for (list = window->paint_stack; list != NULL; list = list->next)
@@ -2796,6 +2778,23 @@ gdk_window_begin_paint_region (GdkWindow       *window,
                                     paint->region);
 }
 
+/* This returns either the current working surface on the paint stack
+ * or the actual impl surface of the window. This should not be used
+ * from very many places: be careful! */
+static cairo_surface_t *
+get_window_surface (GdkWindow *window)
+{
+  if (window->impl_window->paint_stack)
+    {
+      GdkWindowPaint *paint = window->impl_window->paint_stack->data;
+      return cairo_surface_reference (paint->surface);
+    }
+  else
+    {
+      return gdk_window_ref_impl_surface (window);
+    }
+}
+
 /**
  * gdk_window_end_paint:
  * @window: a #GdkWindow
@@ -2841,14 +2840,18 @@ gdk_window_end_paint (GdkWindow *window)
   window->paint_stack = g_slist_delete_link (window->paint_stack,
                                             window->paint_stack);
 
-
-  if (paint->surface != NULL)
+  if (paint->surface_needs_composite)
     {
+      cairo_surface_t *surface;
+
       cairo_region_get_extents (paint->region, &clip_box);
       full_clip = cairo_region_copy (window->clip_region);
       cairo_region_intersect (full_clip, paint->region);
 
-      cr = gdk_cairo_create (window);
+      surface = get_window_surface (window);
+      cr = cairo_create (surface);
+      cairo_surface_destroy (surface);
+
       cairo_set_source_surface (cr, paint->surface, 0, 0);
       gdk_cairo_region (cr, full_clip);
       cairo_clip (cr);
@@ -2866,10 +2869,10 @@ gdk_window_end_paint (GdkWindow *window)
 
       cairo_destroy (cr);
       cairo_region_destroy (full_clip);
-
-      cairo_surface_destroy (paint->surface);
     }
 
+  cairo_surface_destroy (paint->surface);
+
   cairo_region_destroy (paint->region);
   g_free (paint);
 
@@ -2908,8 +2911,7 @@ gdk_window_free_paint_stack (GdkWindow *window)
        {
          GdkWindowPaint *paint = tmp_list->data;
 
-         if (tmp_list == window->paint_stack &&
-             paint->surface != NULL)
+         if (tmp_list == window->paint_stack)
            cairo_surface_destroy (paint->surface);
 
          cairo_region_destroy (paint->region);
@@ -3012,7 +3014,7 @@ gdk_window_clear_backing_region (GdkWindow *window,
   int x_offset = 0, y_offset = 0;
   cairo_t *cr;
 
-  if (GDK_WINDOW_DESTROYED (window) || paint->surface == NULL)
+  if (GDK_WINDOW_DESTROYED (window))
     return;
 
   cr = cairo_create (paint->surface);
@@ -3047,88 +3049,23 @@ gdk_window_clear_backing_region (GdkWindow *window,
   cairo_region_destroy (clip);
 }
 
-static void
-gdk_window_drop_cairo_surface (GdkWindow *window)
-{
-  if (window->cairo_surface)
-    {
-      cairo_surface_finish (window->cairo_surface);
-      cairo_surface_set_user_data (window->cairo_surface, &gdk_window_cairo_key,
-                                  NULL, NULL);
-      window->cairo_surface = NULL;
-    }
-}
-
-static void
-gdk_window_cairo_surface_destroy (void *data)
-{
-  GdkWindow *window = data;
-
-  window->cairo_surface = NULL;
-}
-
-static cairo_surface_t *
-gdk_window_create_cairo_surface (GdkWindow *window,
-                                int width,
-                                int height)
-{
-  cairo_surface_t *surface, *subsurface;
-
-  surface = gdk_window_ref_impl_surface (window);
-  if (gdk_window_has_impl (window))
-    return surface;
-
-  subsurface = cairo_surface_create_for_rectangle (surface,
-                                                   window->abs_x,
-                                                   window->abs_y,
-                                                   width,
-                                                   height);
-  cairo_surface_destroy (surface);
-  return subsurface;
-}
-
-
+/* This is used in places like gdk_cairo_set_source_window and
+ * other places to take "screenshots" of windows. Thus, we allow
+ * it to be used outside of a begin_paint / end_paint. */
 cairo_surface_t *
 _gdk_window_ref_cairo_surface (GdkWindow *window)
 {
   cairo_surface_t *surface;
-  GdkWindowPaint *paint;
 
   g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
 
-  paint = NULL;
-  if (window->impl_window->paint_stack)
-    paint = window->impl_window->paint_stack->data;
-
-  if (paint && paint->surface != NULL)
-    {
-      surface = cairo_surface_create_for_rectangle (paint->surface,
-                                                   window->abs_x,
-                                                   window->abs_y,
-                                                   window->width,
-                                                   window->height);
-    }
-  else
-    {
-      if (!window->cairo_surface)
-       {
-         window->cairo_surface = gdk_window_create_cairo_surface (window,
-                                                                   window->width,
-                                                                   window->height);
-
-         if (window->cairo_surface)
-           {
-             cairo_surface_set_user_data (window->cairo_surface, &gdk_window_cairo_key,
-                                          window, gdk_window_cairo_surface_destroy);
-           }
-       }
-      else
-       cairo_surface_reference (window->cairo_surface);
-
-      surface = window->cairo_surface;
-    }
+  surface = get_window_surface (window);
 
-  return surface;
+  return cairo_surface_create_for_rectangle (surface,
+                                             window->abs_x,
+                                             window->abs_y,
+                                             window->width,
+                                             window->height);
 }
 
 /**
@@ -3149,30 +3086,34 @@ cairo_t *
 gdk_cairo_create (GdkWindow *window)
 {
   GdkWindowPaint *paint;
-  cairo_surface_t *surface;
   cairo_region_t *region;
   cairo_t *cr;
 
   g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
 
-  surface = _gdk_window_ref_cairo_surface (window);
-  cr = cairo_create (surface);
-
-  if (window->impl_window->paint_stack)
+  if (window->impl_window->paint_stack == NULL)
     {
-      paint = window->impl_window->paint_stack->data;
+      cairo_surface_t *dummy_surface;
+      cairo_t *cr;
 
-      region = cairo_region_copy (paint->region);
-      cairo_region_translate (region, -window->abs_x, -window->abs_y);
-      gdk_cairo_region (cr, region);
-      cairo_region_destroy (region);
-   }
-  else
-    gdk_cairo_region (cr, window->clip_region);
+      g_warning ("gdk_cairo_create called from outside a paint. Make sure to call "
+                 "gdk_window_begin_paint_region before calling gdk_cairo_create!");
 
-  cairo_clip (cr);
+      /* Return a dummy surface to keep apps from crashing. */
+      dummy_surface = cairo_image_surface_create (gdk_window_get_content (window), 0, 0);
+      cr = cairo_create (dummy_surface);
+      cairo_surface_destroy (dummy_surface);
+      return cr;
+    }
 
-  cairo_surface_destroy (surface);
+  paint = window->impl_window->paint_stack->data;
+
+  cr = cairo_create (paint->surface);
+  region = cairo_region_copy (paint->region);
+  cairo_region_translate (region, -window->abs_x, -window->abs_y);
+  gdk_cairo_region (cr, region);
+  cairo_region_destroy (region);
+  cairo_clip (cr);
 
   return cr;
 }
index b1e9290bc15d005b1021354b57c170aeeb6b0de4..694a244761727d9c831ff8ad92d5dcc8ca7788d7 100644 (file)
@@ -158,15 +158,6 @@ struct _GdkWindowImplClass
   */
   void         (*destroy_foreign)       (GdkWindow       *window);
 
-  /* Resizes @surface to a new size. If successful, return %TRUE.
-   * If the backend cannot resize surfaces, return %FALSE and a new
-   * surface will be created instead.
-   */
-  gboolean     (* resize_cairo_surface) (GdkWindow       *window,
-                                         cairo_surface_t *surface,
-                                         gint             width,
-                                         gint             height);
-
   /* optional */
   gboolean     (* beep)                 (GdkWindow       *window);
 
index 84e7ab871d33098701c8d54fca22d7c40fff79d4..37efa7ccca783f496d680629f3e7b77c1e59cdc2 100644 (file)
@@ -1104,16 +1104,6 @@ gdk_quartz_window_destroy (GdkWindow *window,
     }
 }
 
-static gboolean
-gdk_window_quartz_resize_cairo_surface (GdkWindow       *window,
-                                        cairo_surface_t *surface,
-                                        gint             width,
-                                        gint             height)
-{
-  /* Quartz surfaces cannot be resized */
-  return FALSE;
-}
-
 static void
 gdk_quartz_window_destroy_foreign (GdkWindow *window)
 {
@@ -2949,7 +2939,6 @@ gdk_window_impl_quartz_class_init (GdkWindowImplQuartzClass *klass)
   impl_class->queue_antiexpose = gdk_quartz_window_queue_antiexpose;
   impl_class->destroy = gdk_quartz_window_destroy;
   impl_class->destroy_foreign = gdk_quartz_window_destroy_foreign;
-  impl_class->resize_cairo_surface = gdk_window_quartz_resize_cairo_surface;
   impl_class->get_shape = gdk_quartz_window_get_shape;
   impl_class->get_input_shape = gdk_quartz_window_get_input_shape;
   impl_class->begin_paint_region = gdk_window_impl_quartz_begin_paint_region;
index 461d1ce9d78b6fb6ce5f8fd1732895a4f47da17a..a3058de8470d8f6c2c8fd4bacef65599928c92be 100644 (file)
@@ -1350,16 +1350,6 @@ gdk_window_wayland_destroy_foreign (GdkWindow *window)
 {
 }
 
-static gboolean
-gdk_window_wayland_resize_cairo_surface (GdkWindow       *window,
-                                         cairo_surface_t *surface,
-                                         gint             width,
-                                         gint             height)
-{
-  /* cairo image surfaces cannot be resized */
-  return FALSE;
-}
-
 static cairo_region_t *
 gdk_wayland_window_get_shape (GdkWindow *window)
 {
@@ -1994,7 +1984,6 @@ _gdk_window_impl_wayland_class_init (GdkWindowImplWaylandClass *klass)
   impl_class->queue_antiexpose = gdk_wayland_window_queue_antiexpose;
   impl_class->destroy = gdk_wayland_window_destroy;
   impl_class->destroy_foreign = gdk_window_wayland_destroy_foreign;
-  impl_class->resize_cairo_surface = gdk_window_wayland_resize_cairo_surface;
   impl_class->get_shape = gdk_wayland_window_get_shape;
   impl_class->get_input_shape = gdk_wayland_window_get_input_shape;
   impl_class->begin_paint_region = gdk_window_impl_wayland_begin_paint_region;
index aa8f5916ea6f8c162044ab7b1bb0612c432b577d..23e1848c0221da1f4c7e3da9782b8758499689c2 100644 (file)
@@ -796,16 +796,6 @@ gdk_win32_window_destroy (GdkWindow *window,
     }
 }
 
-static gboolean
-gdk_win32_window_resize_cairo_surface (GdkWindow       *window,
-                                       cairo_surface_t *surface,
-                                       gint             width,
-                                       gint             height)
-{
-  /* XXX: Make Cairo surface use DC clip */
-  return FALSE;
-}
-
 static void
 gdk_win32_window_destroy_foreign (GdkWindow *window)
 {
@@ -3433,7 +3423,6 @@ gdk_window_impl_win32_class_init (GdkWindowImplWin32Class *klass)
   impl_class->queue_antiexpose = _gdk_win32_window_queue_antiexpose;
   impl_class->destroy = gdk_win32_window_destroy;
   impl_class->destroy_foreign = gdk_win32_window_destroy_foreign;
-  impl_class->resize_cairo_surface = gdk_win32_window_resize_cairo_surface;
   impl_class->get_shape = gdk_win32_window_get_shape;
   //FIXME?: impl_class->get_input_shape = gdk_win32_window_get_input_shape;
 
index 1fa3afc2aa1a5b5ad217ca581278aadda5b554a2..3c7c6cc59d24344e4c78393751b41b9690e515a5 100644 (file)
@@ -1337,17 +1337,6 @@ gdk_x11_window_destroy (GdkWindow *window,
     XDestroyWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window));
 }
 
-static gboolean
-gdk_window_x11_resize_cairo_surface (GdkWindow       *window,
-                                     cairo_surface_t *surface,
-                                     gint             width,
-                                     gint             height)
-{
-  cairo_xlib_surface_set_size (surface, width, height);
-
-  return TRUE;
-}
-
 static void
 gdk_x11_window_destroy_foreign (GdkWindow *window)
 {
@@ -5780,7 +5769,6 @@ gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
   impl_class->queue_antiexpose = _gdk_x11_window_queue_antiexpose;
   impl_class->destroy = gdk_x11_window_destroy;
   impl_class->destroy_foreign = gdk_x11_window_destroy_foreign;
-  impl_class->resize_cairo_surface = gdk_window_x11_resize_cairo_surface;
   impl_class->get_shape = gdk_x11_window_get_shape;
   impl_class->get_input_shape = gdk_x11_window_get_input_shape;
   impl_class->beep = gdk_x11_window_beep;